/*
 *  Copyright (C) 2025 ameise <ameise.wang@gmail.com> - All Rights Reserved
 *
 *  This file is part of e3utils.
 *
 *  e3utils is free software: you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation, either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  e3utils is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with e3utils. If not, see <https://www.gnu.org/licenses/>.
 */

package bytes_buffer

import (
	"gitee.com/ameise84/e3utils/math"
	"gitee.com/ameise84/e3utils/str_conv"
	"io"
)

type RingBuffer struct {
	bytes       []byte //字节
	cap         int    //容量
	maxCap      int
	vrIndex     int  //虚读位置
	rIndex      int  //已读位置
	wIndex      int  //已写位置
	hasData     bool //是否有数据
	isInVirtual bool //是否在虚读状态
	autoGrow    bool //是否自动增长
}

func NewRingBuffer(cap int, maxCap int, autoGrow bool) RingBuffer {
	if maxCap != 0 && maxCap < cap {
		maxCap = cap
	}
	if !autoGrow && maxCap == 0 {
		maxCap = cap
	}

	return RingBuffer{
		bytes:    make([]byte, cap),
		cap:      cap,
		maxCap:   maxCap,
		autoGrow: autoGrow,
	}
}

func (ts *RingBuffer) IsCanGrow() bool {
	return ts.autoGrow
}

func (ts *RingBuffer) GetMaxCapacity() int {
	return ts.maxCap
}

func (ts *RingBuffer) GetCapacity() int {
	return ts.cap
}

func (ts *RingBuffer) GetDataSize() int {
	n := ts.wIndex - ts.rIndex
	if n == 0 {
		if ts.hasData {
			return ts.cap
		} else {
			return 0
		}
	}
	if n > 0 {
		return n
	}
	return ts.cap + n
}

func (ts *RingBuffer) getVirtualDataSize() int {
	n := ts.wIndex - ts.vrIndex
	if n == 0 {
		if ts.hasData {
			return ts.cap
		} else {
			return 0
		}
	}
	if n > 0 {
		return n
	}
	return ts.cap + n
}

func (ts *RingBuffer) GetEmptySize() int {
	return ts.cap - ts.GetDataSize()
}

func (ts *RingBuffer) IsEmpty() bool {
	return !ts.hasData
}

func (ts *RingBuffer) Clean() {
	ts.rIndex = 0
	ts.vrIndex = 0
	ts.wIndex = 0
	ts.hasData = false
	ts.isInVirtual = false
}

func (ts *RingBuffer) GrowTo(cap int) error {
	if cap <= 0 {
		return ErrReserveZero
	}
	if cap == ts.cap {
		return nil
	}
	if ts.maxCap != 0 && cap > ts.maxCap {
		return ErrReserveBigger
	}
	if cap < ts.GetDataSize() {
		return ErrReserveSmaller
	}

	newBytes := make([]byte, cap)
	if ts.hasData {
		if ts.wIndex > ts.rIndex {
			copy(newBytes, ts.bytes[ts.rIndex:ts.wIndex])
			ts.vrIndex -= ts.rIndex
			ts.wIndex -= ts.rIndex
			ts.rIndex = 0
		} else {
			tailSize := ts.cap - ts.rIndex
			copy(newBytes, ts.bytes[ts.rIndex:])
			copy(newBytes[tailSize:], ts.bytes[:ts.wIndex])

			if ts.isInVirtual {
				if ts.vrIndex > ts.rIndex {
					ts.vrIndex = ts.vrIndex - ts.rIndex
				} else {
					ts.vrIndex = tailSize + ts.vrIndex
				}
			} else {
				ts.vrIndex = 0
			}
			ts.wIndex = tailSize + ts.wIndex
			ts.rIndex = 0
		}
	}
	ts.bytes = newBytes
	ts.cap = cap
	return nil
}

func (ts *RingBuffer) GrowLen(add int) (int, error) {
	newCap := ts.cap + add
	err := ts.GrowTo(newCap)
	return ts.cap, err
}

func (ts *RingBuffer) AssignString(s string) error {
	ts.Clean()
	return ts.AppendBytes(str_conv.ToBytes(s))
}

func (ts *RingBuffer) AssignBytes(p []byte) error {
	ts.Clean()
	return ts.AppendBytes(p)
}

func (ts *RingBuffer) AssignByte(p byte) error {
	return ts.AssignBytes([]byte{p})
}

func (ts *RingBuffer) AppendString(s string) error {
	return ts.AppendBytes(str_conv.ToBytes(s))
}

func (ts *RingBuffer) AppendBytes(p []byte) error {
	dataSize := len(p)
	if dataSize == 0 {
		return nil
	}
	usedSize := ts.GetDataSize()
	toSize := usedSize + dataSize
	if ts.maxCap != 0 && toSize > ts.maxCap {
		return io.ErrShortWrite
	}
	if toSize > ts.cap {
		if !ts.autoGrow {
			return io.ErrShortWrite
		}
		toCap := math.CeilToPowerOfTwo(dataSize + ts.cap)
		if ts.maxCap != 0 && toCap > ts.maxCap {
			toCap = ts.maxCap
		}
		_ = ts.GrowTo(toCap)
	}

	n := copy(ts.bytes[ts.wIndex:], p)
	if n != dataSize {
		k := copy(ts.bytes[:], p[n:])
		ts.wIndex = k
	} else {
		ts.hasData = true
		ts.wIndex += n
	}
	return nil
}

func (ts *RingBuffer) AppendByte(p byte) error {
	return ts.AppendBytes([]byte{p})
}

func (ts *RingBuffer) WriteString(s string) (int, error) {
	return ts.Write(str_conv.ToBytes(s))
}

func (ts *RingBuffer) Write(p []byte) (int, error) {
	dataSize := len(p)
	emptySize := ts.GetEmptySize()
	if emptySize < dataSize && ts.IsCanGrow() {
		toCap := math.CeilToPowerOfTwo(ts.cap + dataSize - emptySize)
		if ts.maxCap != 0 && toCap > ts.maxCap {
			toCap = ts.maxCap
		}
		_ = ts.GrowTo(toCap)
		emptySize = ts.GetEmptySize()
	}
	if emptySize >= dataSize {
		_ = ts.AppendBytes(p)
		return dataSize, nil
	} else {
		_ = ts.AppendBytes(p[:emptySize])
		return emptySize, io.ErrShortWrite
	}
}

func (ts *RingBuffer) Peek() (first, end []byte, err error) {
	if !ts.hasData {
		return nil, nil, io.EOF
	}
	if ts.wIndex > ts.rIndex {
		return ts.bytes[ts.rIndex:ts.wIndex], nil, nil
	} else {
		return ts.bytes[ts.rIndex:], ts.bytes[:ts.wIndex], nil
	}
}

func (ts *RingBuffer) PeekLen(outLen int) (first, end []byte, err error) {
	if !ts.hasData {
		return nil, nil, io.EOF
	}
	if ts.GetDataSize() < outLen {
		return nil, nil, io.ErrUnexpectedEOF
	}
	rTo := ts.rIndex + outLen
	if rTo > ts.cap {
		return ts.bytes[ts.rIndex:], ts.bytes[:rTo-ts.cap], nil
	} else {
		return ts.bytes[ts.rIndex:rTo], nil, nil
	}
}

func (ts *RingBuffer) VirtualFetchLen(outLen int) (first, end []byte, err error) {
	if outLen == 0 {
		return nil, nil, ErrVirtualReadLen
	}
	if ts.vrIndex == ts.wIndex && ts.isInVirtual {
		return nil, nil, io.EOF
	}
	if ts.getVirtualDataSize() < outLen {
		return nil, nil, io.ErrUnexpectedEOF
	}

	vrTo := ts.vrIndex + outLen
	if vrTo > ts.cap {
		vrTo = vrTo - ts.cap
		first = ts.bytes[ts.vrIndex:]
		end = ts.bytes[:vrTo]
	} else {
		first = ts.bytes[ts.vrIndex:vrTo]
	}
	ts.vrIndex = vrTo
	ts.isInVirtual = true
	return
}

func (ts *RingBuffer) VirtualCommit() {
	if ts.isInVirtual {
		if ts.vrIndex == ts.wIndex {
			ts.Clean()
		} else {
			ts.rIndex = ts.vrIndex
			ts.isInVirtual = false
		}
	}
}

func (ts *RingBuffer) VirtualRecover() {
	ts.vrIndex = ts.rIndex
	ts.isInVirtual = false
}

func (ts *RingBuffer) Fetch() (first, end []byte, err error) {
	if ts.isInVirtual {
		return nil, nil, ErrInVirtualRead
	}

	if !ts.hasData {
		return nil, nil, io.EOF
	}
	if ts.wIndex > ts.rIndex {
		first = ts.bytes[ts.rIndex:ts.wIndex]
	} else {
		first = ts.bytes[ts.rIndex:]
		end = ts.bytes[:ts.wIndex]
	}
	ts.Clean()
	return
}

func (ts *RingBuffer) FetchLen(outLen int) (first, end []byte, err error) {
	if ts.isInVirtual {
		return nil, nil, ErrInVirtualRead
	}
	if !ts.hasData {
		return nil, nil, io.EOF
	}
	if ts.GetDataSize() < outLen {
		return nil, nil, io.ErrUnexpectedEOF
	}
	rTo := ts.rIndex + outLen
	if rTo > ts.cap {
		rTo -= ts.cap
		first = ts.bytes[ts.rIndex:]
		end = ts.bytes[:rTo]
	} else {
		first = ts.bytes[ts.rIndex:rTo]
	}

	if rTo == ts.wIndex {
		ts.Clean()
	} else {
		ts.rIndex = rTo
		ts.vrIndex = rTo
	}
	return
}

func (ts *RingBuffer) ReadAll(out []byte) (int, error) {
	dataSize := ts.GetDataSize()
	if len(out) < ts.GetDataSize() {
		return 0, io.ErrShortBuffer
	}
	first, end, err := ts.Fetch()
	if err != nil {
		return 0, err
	}
	_ = copy(out, first)
	if end != nil {
		_ = copy(out[len(first):], end)
	}
	return dataSize, nil
}

func (ts *RingBuffer) ReadLen(out []byte, outLen int) error {
	if len(out) < outLen {
		return io.ErrShortBuffer
	}
	first, end, err := ts.FetchLen(outLen)
	if err != nil {
		return err
	}
	copy(out, first)
	if end != nil {
		_ = copy(out[len(first):], end)
	}
	return nil
}

func (ts *RingBuffer) Read(out []byte) (int, error) {
	if ts.isInVirtual {
		return 0, ErrInVirtualRead
	}
	if !ts.hasData {
		return 0, io.EOF
	}
	if ts.wIndex > ts.rIndex {
		size := copy(out, ts.bytes[ts.rIndex:ts.wIndex])
		ts.rIndex += size
		return size, nil
	} else {
		size1 := copy(out, ts.bytes[ts.rIndex:])
		if size1 == len(out) {
			return size1, nil
		}
		size2 := copy(out[size1:], ts.bytes[:ts.wIndex])
		ts.rIndex += size1 + size2
		ts.rIndex %= ts.cap
		return size1 + size2, nil
	}
}
